﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX;
using System.IO;
using System.Text.RegularExpressions;
using gpuHough.DirectX;

namespace gpuHough
{
    public partial class HoughForm : Form
    {   
        TexturedRect.Collection rectangles = new TexturedRect.Collection();
        TexturedRect shaderedRect;
        Effect thresholdEffect;
        
        int targetSize = 512;
        int sourceSize = 512;
        int n = 32;
        int anizotropy = 16;
        
        bool dataExpired = false;
        
        //
        public HoughForm()
        {
            InitializeComponent();
            dxPanel.Width = targetSize;
            CreateData();
            shaderedRect = new TexturedRect(new Vector2(1f, 1f), new Vector2(0f, 0f), new Vector2(1f, 1f), new Vector2(0f, 0f), 0);
        }

        private void CreateCirclesData()
        {
            double rAbs = tbR.Value / 10.0;

            float r = (float)rAbs / targetSize;

            for (int i = 0; i < n; i++)
            {
                double angle = 2 * Math.PI / n * i;
                rectangles.Add(new TexturedRect(new Vector2(1f, 1f), new Vector2((float)(Math.Cos(angle) * r), (float)(Math.Sin(angle) * r)), new Vector2(1f, 1f), new Vector2(0f, 0f), 0));
            }
        }

        private void CreateLinesData()
        {
            double sourceRectPixelWidth = sourceSize / n;
            double targetRectPixelWidth = Math.Ceiling(sourceRectPixelWidth / anizotropy);
            int angleValues = (int)Math.Floor(targetSize / targetRectPixelWidth);
            float sourceRectWidth = (float)(sourceRectPixelWidth / sourceSize);
            float tarectRectWidth = (float)(targetRectPixelWidth / targetSize);

            for (int angleNumber = 0; angleNumber < angleValues; angleNumber++)
            {
                double angle = Math.PI / angleValues * angleNumber;
                float targetShift = tarectRectWidth * angleNumber;

                for (int sliceNumber = 0; sliceNumber < n; sliceNumber++)
                {
                    float sourceShift = sourceRectWidth * sliceNumber;
                    rectangles.Add(new TexturedRect(new Vector2(1f, tarectRectWidth), new Vector2(0f, targetShift), new Vector2(1f, sourceRectWidth), new Vector2(0f, sourceShift), (float)angle));
                }
            }   
        }

        private void CreateData()
        {
            rectangles.Clear();
            n = 1 << tbCount.Value;

            if (rbCircles.Checked)
                CreateCirclesData();
            else
                CreateLinesData();

            dataExpired = false;
        }

        private void CreateTextures()
        {
            try
            {
                dxPanel.SourceTexture = dxPanel.MakeTextureFromFile(Path.Combine("Resources", (string)cbSource.SelectedItem), sourceSize, sourceSize, 1, Usage.None, cbGray.Checked ? Format.L8 : Format.X8R8G8B8, Pool.Managed, Filter.Triangle, Filter.None, 0);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error opening file: " + ex.Message);
            }

            dxPanel.TargetTexture = dxPanel.MakeTexture(targetSize, targetSize, 1, Usage.RenderTarget, cbGray.Checked ? Format.L8 : Format.X8R8G8B8, Pool.Default);
        }

        private void dxPanel_Load(object sender, DXEventArgs e)
        {
            dxPanel.SetOrthoViewProjection();
            dxPanel.PermanentOptions = new Options()
            {   
            };

            cbSource.Items.AddRange(Directory.GetFiles("Resources", "*.png").Select(path => Path.GetFileName(path)).ToArray());
            cbSize.SelectedItem = "@ 512";
            cbSource.SelectedItem = "circlesMid.png";
            
            //dxPanel.SourceTexture = TextureLoader.FromFile("screenshot.png", 512, 512, 1, Usage.None, Format.A8B8G8R8, Pool.Default, Filter.None, Filter.None, 0);
            thresholdEffect = dxPanel.MakeEffectFromFile("Resources/threshold.fx", null, null, ShaderFlags.None, null);
        }

        private void dxPanel_Paint(object sender, DXEventArgs e)
        {
            this.Text = dxPanel.FPS.ToString("0") + " fps";

            if (dataExpired) CreateData();

            //render rectangles
            dxPanel.RenderToTexture(new Options()
            {
                AlphaBlendEnable = true,
                BlendFactor = (byte)(256.0 * (tbBrightness.Value / 100.0) / n)
            },
                rectangles);

            dxPanel.SwapTextures();

            //process result with shaders

            dxPanel.RenderToScreen(new Options()
            {
                Effect = cbThreshold.Checked ? thresholdEffect : null
                //Effect = null
            },
                shaderedRect); 
            
            dxPanel.SwapTextures();
        }

        #region "Controls events"

        private void tbR_Scroll(object sender, EventArgs e)
        {
            dataExpired = true;
            lblR.Text = Regex.Replace(lblR.Text, @"(\d|\.|\,)+", (tbR.Value / 10.0).ToString("f1"));
        }

        private void tbCount_Scroll(object sender, EventArgs e)
        {
            dataExpired = true;
            lblCount.Text = Regex.Replace(lblCount.Text, @"(\d|\.|\,)+", (1 << tbCount.Value).ToString());
        }

        private void tbBrightness_Scroll(object sender, EventArgs e)
        {   
            lblBrightness.Text = Regex.Replace(lblBrightness.Text, @"(\d|\.|\,)+", tbBrightness.Value.ToString());
        }

        private void option_SelectedIndexChanged(object sender, EventArgs e)
        {
            CreateTextures();
        }

        private void cbSize_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cbSource.SelectedItem != null)
            {
                sourceSize = int.Parse(((string)cbSize.SelectedItem).TrimStart('@'));
                CreateTextures();
                dataExpired = true;
            }
        }

        private void rbCircles_CheckedChanged(object sender, EventArgs e)
        {
            CreateTextures();
            dataExpired = true;
            lblR.Visible = tbR.Visible = rbCircles.Checked;
        }

        #endregion
    }
}
